import React, { useContext, useEffect, useRef, useState } from 'react';
import { useSize } from 'ahooks';
import * as BABYLON from 'babylonjs';
import { Button } from "antd";

import { Application } from "@yulintu/freesia-bootstrap";

import "./index.less";
import Animator from '../../framework/Animator';

const Lines = function (props) {

    const context = useContext(Application.Context);
    const canvas = useRef();

    const [engine, setEngine] = useState();
    const [scene, setScene] = useState();

    const [animatorY] = useState(new Animator());
    const [animatorAlpha] = useState(new Animator());
    const [animatorScale] = useState(new Animator());

    const [animatorTrail] = useState(new Animator());

    useEffect(() => {
        const engine = new BABYLON.Engine(canvas.current, true);
        setEngine(engine);
    }, []);

    useEffect(() => {

        if (!engine)
            return;

        const scene = new BABYLON.Scene(engine);
        // scene.clearColor = new BABYLON.Color4(0, 0, 0, 1);
        setScene(scene);

        const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 50, new BABYLON.Vector3(0, 0, 0));

        camera.attachControl(canvas, true);
        const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 10, 0));
        const light2 = new BABYLON.DirectionalLight("dir02", new BABYLON.Vector3(0, -1, 0), scene);
        light2.position = new BABYLON.Vector3(0, 100, 0);

        let faceUV = [];
        faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0); //rear face
        faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0); //rear face
        faceUV[2] = new BABYLON.Vector4(0.25, 0.0, 0.5, 1.0); //rear face
        faceUV[3] = new BABYLON.Vector4(0.75, 0.0, 1, 1.0); //rear face

        const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", { size: 2000, faceUV, wrap: true });
        skybox.position.y = 950;
        const skyboxMaterial = new BABYLON.StandardMaterial("skyBox");
        skyboxMaterial.backFaceCulling = false;
        skyboxMaterial.diffuseTexture = new BABYLON.Texture("/data/skybox/1.png");
        // skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SPHERICAL_MODE;
        // skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
        skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
        skybox.material = skyboxMaterial;

        const groundMat = new BABYLON.StandardMaterial("groundMat");
        groundMat.diffuseTexture = new BABYLON.Texture("/data/ground/1.jpg");
        groundMat.diffuseTexture.uScale = 200.0;
        groundMat.diffuseTexture.vScale = 200.0;
        groundMat.specularColor = new BABYLON.Color3(0, 0, 0);

        const ground = BABYLON.MeshBuilder.CreateGround("largeGround", { width: 2000, height: 2000 });
        ground.material = groundMat;
        ground.position.y = -2;


        const largeGroundMat = new BABYLON.StandardMaterial("largeGroundMat");
        largeGroundMat.diffuseTexture = new BABYLON.Texture("/data/dem/无标题.png");
        largeGroundMat.specularColor = new BABYLON.Color3(0, 0, 0);
        largeGroundMat.alpha = 0;

        const largeGround = BABYLON.MeshBuilder.CreateGroundFromHeightMap("largeGround", "/data/dem/tif3.png", { width: 36.01, height: 36.01, subdivisions: 30, minHeight: 0, maxHeight: 5 });
        largeGround.material = largeGroundMat;
        largeGround.position.y = -5;

        const boxMat = new BABYLON.StandardMaterial("largeGroundMat");
        boxMat.diffuseTexture = new BABYLON.Texture("/data/dem/land.jpg");
        boxMat.alpha = 0;

        faceUV = [];
        faceUV[0] = new BABYLON.Vector4(0.0, 0.0, 15.0, 1.0); //rear face
        faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 15.0, 1.0); //rear face
        faceUV[2] = new BABYLON.Vector4(0.0, 0.0, 15.0, 1.0); //rear face
        faceUV[3] = new BABYLON.Vector4(0.0, 0.0, 15.0, 1.0); //rear face

        const box = BABYLON.MeshBuilder.CreateBox("box", {
            width: 36.01,
            depth: 36.01,
            height: 3,
            faceUV: faceUV,
            wrap: true
        });
        box.position.y = -6.51;
        box.material = boxMat;

        animatorY.onChanged(e => {
            largeGround.position.y = e.groundY;
            box.position.y = e.boxY;
        })

        animatorAlpha.onChanged(e => {
            largeGroundMat.alpha = e.alpha;
            boxMat.alpha = e.alpha;
        })

        animatorScale.onChanged(e => {
            largeGround.scaling.y = e.scale;
        })




        var line2D = function (name, options, scene) {

            //Arrays for vertex positions and indices
            var positions = [];
            var indices = [];
            var normals = [];
            var standardUV;
            var width = options.width || 1;
            var path = options.path;
            var closed = options.closed || false;
            if (options.standardUV === undefined) {
                standardUV = true;
            }
            else {
                standardUV = options.standardUV;
            }

            var interiorIndex;

            //Arrays to hold wall corner data 
            var innerBaseCorners = [];
            var outerBaseCorners = [];

            var outerData = [];
            var innerData = [];
            var angle = 0;
            var lineNormal;
            var direction;
            var nbPoints = path.length;
            var line = BABYLON.Vector3.Zero();
            var nextLine = BABYLON.Vector3.Zero();
            path[1].subtractToRef(path[0], line);

            if (nbPoints > 2 && closed) {
                path[2].subtractToRef(path[1], nextLine);
                for (var p = 0; p < nbPoints; p++) {
                    angle = Math.PI - Math.acos(BABYLON.Vector3.Dot(line, nextLine) / (line.length() * nextLine.length()));
                    direction = BABYLON.Vector3.Cross(line, nextLine).normalize().y;
                    lineNormal = new BABYLON.Vector3(-line.z, 0, 1 * line.x).normalize();
                    line.normalize();
                    innerData[(p + 1) % nbPoints] = path[(p + 1) % nbPoints];
                    outerData[(p + 1) % nbPoints] = path[(p + 1) % nbPoints].add(lineNormal.scale(width)).add(line.scale(direction * width / Math.tan(angle / 2)));
                    line = nextLine.clone();
                    path[(p + 3) % nbPoints].subtractToRef(path[(p + 2) % nbPoints], nextLine);
                }
            }
            else {
                lineNormal = new BABYLON.Vector3(-line.z, 0, 1 * line.x).normalize();
                line.normalize();
                innerData[0] = path[0];
                outerData[0] = path[0].add(lineNormal.scale(width));

                for (var p = 0; p < nbPoints - 2; p++) {
                    path[p + 2].subtractToRef(path[p + 1], nextLine);
                    angle = Math.PI - Math.acos(BABYLON.Vector3.Dot(line, nextLine) / (line.length() * nextLine.length()));
                    direction = BABYLON.Vector3.Cross(line, nextLine).normalize().y;
                    lineNormal = new BABYLON.Vector3(-line.z, 0, 1 * line.x).normalize();
                    line.normalize();
                    innerData[p + 1] = path[p + 1];
                    outerData[p + 1] = path[p + 1].add(lineNormal.scale(width)).add(line.scale(direction * width / Math.tan(angle / 2)));
                    line = nextLine.clone();
                }
                if (nbPoints > 2) {
                    path[nbPoints - 1].subtractToRef(path[nbPoints - 2], line);
                    lineNormal = new BABYLON.Vector3(-line.z, 0, 1 * line.x).normalize();
                    line.normalize();
                    innerData[nbPoints - 1] = path[nbPoints - 1];
                    outerData[nbPoints - 1] = path[nbPoints - 1].add(lineNormal.scale(width));
                }
                else {
                    innerData[1] = path[1]
                    outerData[1] = path[1].add(lineNormal.scale(width));
                }
            }

            var maxX = Number.MIN_VALUE;
            var minX = Number.MAX_VALUE;
            var maxZ = Number.MIN_VALUE;
            var minZ = Number.MAX_VALUE;

            for (var p = 0; p < nbPoints; p++) {
                positions.push(innerData[p].x, innerData[p].y, innerData[p].z);
                maxX = Math.max(innerData[p].x, maxX);
                minX = Math.min(innerData[p].x, minX);
                maxZ = Math.max(innerData[p].z, maxZ);
                minZ = Math.min(innerData[p].z, minZ);
            }

            for (var p = 0; p < nbPoints; p++) {
                positions.push(outerData[p].x, outerData[p].y, outerData[p].z);
                maxX = Math.max(innerData[p].x, maxX);
                minX = Math.min(innerData[p].x, minX);
                maxZ = Math.max(innerData[p].z, maxZ);
                minZ = Math.min(innerData[p].z, minZ);
            }

            for (var i = 0; i < nbPoints - 1; i++) {
                indices.push(i, i + 1, nbPoints + i + 1);
                indices.push(i, nbPoints + i + 1, nbPoints + i)
            }

            if (nbPoints > 2 && closed) {
                indices.push(nbPoints - 1, 0, nbPoints);
                indices.push(nbPoints - 1, nbPoints, 2 * nbPoints - 1)
            }

            var normals = [];
            var uvs = [];

            if (standardUV) {
                for (var p = 0; p < positions.length; p += 3) {
                    uvs.push((positions[p] - minX) / (maxX - minX), (positions[p + 2] - minZ) / (maxZ - minZ));
                }
            }
            else {
                var flip = 0;
                var p1 = 0;
                var p2 = 0;
                var p3 = 0;
                var v0 = innerData[0];
                var v1 = innerData[1].subtract(v0);
                var v2 = outerData[0].subtract(v0);
                var v3 = outerData[1].subtract(v0);
                var axis = v1.clone();
                axis.normalize();

                p1 = BABYLON.Vector3.Dot(axis, v1);
                p2 = BABYLON.Vector3.Dot(axis, v2);
                p3 = BABYLON.Vector3.Dot(axis, v3);
                var minX = Math.min(0, p1, p2, p3);
                var maxX = Math.max(0, p1, p2, p3);

                uvs[2 * indices[0]] = -minX / (maxX - minX);
                uvs[2 * indices[0] + 1] = 1;
                uvs[2 * indices[5]] = (p2 - minX) / (maxX - minX);
                uvs[2 * indices[5] + 1] = 0;

                uvs[2 * indices[1]] = (p1 - minX) / (maxX - minX);
                uvs[2 * indices[1] + 1] = 1;
                uvs[2 * indices[4]] = (p3 - minX) / (maxX - minX);
                uvs[2 * indices[4] + 1] = 0;

                for (var i = 6; i < indices.length; i += 6) {

                    flip = (flip + 1) % 2;
                    v0 = innerData[0];
                    v1 = innerData[1].subtract(v0);
                    v2 = outerData[0].subtract(v0);
                    v3 = outerData[1].subtract(v0);
                    axis = v1.clone();
                    axis.normalize();

                    p1 = BABYLON.Vector3.Dot(axis, v1);
                    p2 = BABYLON.Vector3.Dot(axis, v2);
                    p3 = BABYLON.Vector3.Dot(axis, v3);
                    var minX = Math.min(0, p1, p2, p3);
                    var maxX = Math.max(0, p1, p2, p3);

                    uvs[2 * indices[i + 1]] = flip + Math.cos(flip * Math.PI) * (p1 - minX) / (maxX - minX);
                    uvs[2 * indices[i + 1] + 1] = 1;
                    uvs[2 * indices[i + 4]] = flip + Math.cos(flip * Math.PI) * (p3 - minX) / (maxX - minX);
                    uvs[2 * indices[i + 4] + 1] = 0;
                }
            }

            BABYLON.VertexData.ComputeNormals(positions, indices, normals);
            BABYLON.VertexData._ComputeSides(BABYLON.Mesh.DOUBLESIDE, positions, indices, normals, uvs);
            console.log(uvs)
            //Create a custom mesh  
            var customMesh = new BABYLON.Mesh("custom", scene);

            //Create a vertexData object
            var vertexData = new BABYLON.VertexData();

            //Assign positions and indices to vertexData
            vertexData.positions = positions;
            vertexData.indices = indices;
            vertexData.normals = normals;
            vertexData.uvs = uvs;

            //Apply vertexData to custom mesh
            vertexData.applyToMesh(customMesh);

            return customMesh;

        }
        var path = [
            new BABYLON.Vector3(-18, 0, -18),
            new BABYLON.Vector3(-18, 0, 18),
            new BABYLON.Vector3(18, 0, 18),
            new BABYLON.Vector3(18, 0, -18),
            new BABYLON.Vector3(-18.2, 0, -18),
        ];


        var line = line2D("line", { path: path, width: 0.2, standardUV: false }, scene);
        var lineMat2 = new BABYLON.StandardMaterial('lineMat2', scene);
        // sourceMat.specularColor = new BABYLON.Color3.Red();
        lineMat2.diffuseColor = BABYLON.Color3.FromHexString("#FFFFFF");
        lineMat2.alpha = 0.1;
        line.material = lineMat2;

        // var line = BABYLON.MeshBuilder.CreateTube("tube", { path: path, radius: 0.5 }, scene);
        var lineMat = new BABYLON.StandardMaterial('lineMat', scene);
        // sourceMat.specularColor = new BABYLON.Color3.Red();
        lineMat.diffuseColor = BABYLON.Color3.FromHexString("#00FCFF");
        var texture = new BABYLON.Texture("/data/lines/line5.png", scene);
        texture.uScale = 0.5;
        lineMat.opacityTexture = texture;
        lineMat.specularColor = new BABYLON.Color3(0, 0, 0);
        lineMat.emissiveColor = BABYLON.Color3.FromHexString("#00FCFF");
        // line.material = lineMat;

        var glLine = new BABYLON.GlowLayer("glow", scene, {
            mainTextureFixedSize: 1024,
            blurKernelSize: 64
        });

        // glLine.addIncludedOnlyMesh(line);

        scene.onBeforeRenderObservable.add(() => {
            texture.uOffset = (texture.uOffset + 0.002) % 1;
        });



        const tube = BABYLON.MeshBuilder.CreateTube("tube", { path: path, radius: 0.2, invertUV: true, sideOrientation: BABYLON.Mesh.DEFAULTSIDE }, scene);
        tube.material = lineMat;


        const cube = BABYLON.MeshBuilder.CreateSphere('cube', {}, scene);
        cube.scaling.x = 0.5;
        cube.scaling.y = 0.5;
        cube.scaling.z = 0.5;

        var cubeMat = new BABYLON.StandardMaterial('cubeMat', scene);
        cubeMat.alpha = 0;
        cubeMat.diffuseColor = BABYLON.Color3.FromHexString("#00FCFF");
        // sourceMat.specularColor = new BABYLON.Color3.Red();
        cubeMat.emissiveColor = BABYLON.Color3.FromHexString("#00FCFF");
        cube.material = cubeMat;

        var trail = new BABYLON.TrailMesh('trail', cube, scene, 0.5, 30, true);
        var sourceMat = new BABYLON.StandardMaterial('sourceMat', scene);
        sourceMat.alpha = 0.3;
        sourceMat.diffuseColor = BABYLON.Color3.FromHexString("#00FCFF");
        // sourceMat.specularColor = new BABYLON.Color3.Red();
        sourceMat.emissiveColor = BABYLON.Color3.FromHexString("#00FCFF");

        trail.material = sourceMat;

        var gl = new BABYLON.GlowLayer("glow", scene, {
            mainTextureFixedSize: 1024,
            blurKernelSize: 64
        });
        gl.addIncludedOnlyMesh(trail);

        animatorTrail.onChanged(e => {
            cube.position.x = e.x;
            cube.position.z = e.z;
        })

        scene.onPointerObservable.add(e => {

            switch (e.type) {
                case BABYLON.PointerEventTypes.POINTERPICK:
                    console.log(e);
                    if (e.pickInfo.hit) {
                        animatorTrail.from({ x: cube.position.x, z: cube.position.z }).to({ x: e.pickInfo.pickedPoint.x, z: e.pickInfo.pickedPoint.z, }, 2000).start();
                    }

                    break;

                default:
                    break;
            }
        });

        const circle = BABYLON.MeshBuilder.CreateGround("circle", { width: 90, height: 90 });
        var circleMat = new BABYLON.StandardMaterial('circleMat', scene);
        var circleMatTexture = new BABYLON.Texture("/data/lines/circle.png", scene);
        circleMatTexture.hasAlpha = true;
        circleMat.diffuseTexture = circleMatTexture;
        circleMat.useAlphaFromDiffuseTexture = true;
        circle.material = circleMat;
        circle.position.y = -1.9;

        const circle2 = BABYLON.MeshBuilder.CreateGround("circle", { width: 80, height: 80 });
        var circleMat = new BABYLON.StandardMaterial('circleMat', scene);
        var circleMatTexture = new BABYLON.Texture("/data/lines/circle1.png", scene);
        circleMatTexture.hasAlpha = true;
        circleMat.diffuseTexture = circleMatTexture;
        circleMat.useAlphaFromDiffuseTexture = true;
        circle2.material = circleMat;
        circle2.position.y = -1.9;


        scene.onBeforeRenderObservable.add(() => {
            circle.rotation.y += 0.02;
            circle2.rotation.y -= 0.01;
        });


    }, [engine, animatorY, animatorAlpha, animatorScale, animatorTrail]);

    useEffect(() => {
        engine?.runRenderLoop(() => {
            scene?.render();
        });
    }, [engine, scene])

    const size = useSize(canvas.current);

    useEffect(() => {
        engine?.resize();
    }, [engine, size?.width, size?.height])

    return <>
        <div className='babylon-frame'>
            <canvas ref={canvas}></canvas>
            <div className='action-bar'>
                <Button onClick={e => {
                    animatorY.from({ groundY: -5, boxY: -6.51 }).to({ groundY: 0, boxY: -1.51, }, 2000).start();
                    animatorAlpha.from({ alpha: 0 }).to({ alpha: 1 }, 1000).start();
                    animatorScale.from({ scale: 0 }).to({ scale: 1 }, 2000).start();
                }}>出现</Button>
                <Button onClick={e => {
                    animatorY.from({ groundY: 0, boxY: -1.51 }).to({ groundY: -5, boxY: -6.51 }, 2000).start();
                    animatorAlpha.from({ alpha: 1 }).to({ alpha: 0 }, 1000).start();
                    animatorScale.from({ scale: 1 }).to({ scale: 0 }, 2000).start();
                }}>消失</Button>
            </div>
        </div>
    </>
}

export default Lines;